<?php

/**
 * 格式化输出
 * @param $data : 要打印的数据
 * @param bool $exit : 是否终止执行，1=终止，0=继续
 */
function prin($data, $exit = true)
{
	if (is_array($data) or is_object($data)) {
		echo "<pre>" . PHP_EOL;
		print_r($data);
		echo "</pre>" . PHP_EOL;
	} else {
		print_r($data);
	}
	if ($exit) exit;
}

/**
 * 格式化输出
 * @param $data : 要打印的数据
 * @param bool $exit : 是否终止执行，1=终止，0=继续
 */
function vdump($data, $exit = true)
{
	if (is_array($data) or is_object($data)) {
		echo "<pre>" . PHP_EOL;
		var_dump($data);
		echo "</pre>" . PHP_EOL;
	} else {
		var_dump($data);
	}
	if ($exit) exit;
}

/**
 * 判断 $str 是不是 json 字符串
 * @param $str
 * @return false | obj
 */
function is_json($str)
{
	if (!is_string($str)) {
		return false;
	}
	$decode = json_decode($str);
	return $decode === true || $decode === false || is_null($decode) ? false : $decode;
}

/**
 * @return bool
 */
function is_mobile()
{
	$agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
	$match = '/Android|webOS|iPhone|iPad|iPod|iOS|Phone|BlackBerry|mobile|Opera Mini|webmate/i';
	if (preg_match($match, $agent)) {
		return true;
	} elseif (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
		return true;
	} elseif (isset($_SERVER['HTTP_VIA']) and stristr($_SERVER['HTTP_VIA'], "wap")) {
		return true;
	} elseif (isset($_SERVER['HTTP_ACCEPT'])) {
		$ACCEPT = $_SERVER['HTTP_ACCEPT'];
		if ((stripos($ACCEPT, 'vnd.wap.wml') !== false) && (stripos($ACCEPT, 'textml') === false || (stripos($ACCEPT, 'vnd.wap.wml') < stripos($ACCEPT, 'textml')))) {
			return true;
		}
	}
	return false;
}

/**
 * 获取客户端 IP
 * @return string
 */
function get_client_ip()
{
	$request = (new Http())->request();
	$ip = $request->getClientIp();
	return $ip;
}

/**
 * 把 $jsonStr 中的 null值 替换为空字符串
 * @param string $jsonStr
 * @return mixed
 */
function json_replace_null ($jsonStr) {
	$jsonStr = str_ireplace(':null', ':""', $jsonStr);
	return $jsonStr;
}

/**
 * 将下划线式字符串转换为驼峰式
 * @param string $str: 要转换的字符串
 * @param bool $ucfirst: 首字母是否大写，默认为否
 * @return string
 */
function line_to_hump ($str, $ucfirst = false)
{
	$str = ucwords(str_replace('_', ' ', $str));
	$str = str_replace(' ', '', $str);
	$str = $ucfirst ? $str : lcfirst($str);
	return $str;
}

/**
 * 将驼峰式字符串转换为下划线式
 * @param string $str: 要转换的字符串
 * @return string
 */
function hump_to_line ($str)
{
	$str = lcfirst($str);
	$str = preg_replace_callback('#([A-Z]{1,})#', function($matches) {
		return '_' . strtolower($matches[0]);
	}, $str);
	return $str;
}

/**
 * 递归地将数组键名由下划线式改为为驼峰式
 * @param $arrData: 要转换的数组
 * @param bool $ucfirst: 首字母是否大写，默认为否
 * @return array
 */
function key_to_hump ($arrData, $ucfirst = false)
{
	if (empty($arrData) || !is_array($arrData)) {
		return $arrData;
	}
	$newArr = array();
	foreach ($arrData as $k => $v) {
		$k = line_to_hump($k, $ucfirst);
		$newArr[$k] = is_array($v) ? key_to_hump($v, $ucfirst) : $v;
	}
	return $newArr;
}

/**
 * 递归地将数组键名由驼峰式改为为下划线式
 * @param $arrData: 要转换的数组
 * @return array
 */
function key_to_line ($arrData) {
	if (empty($arrData)|| !is_array($arrData)) {
		return $arrData;
	}
	$newArr = array();
	foreach ($arrData as $k => $v) {
		$k = hump_to_line($k);
		$newArr[$k] = is_array($v) ? key_to_line($v) : $v;
	}
	return $newArr;
}

function get_config($key = null, $default = null)
{
	return \sframe\Config::get($key, $default);
}

if (!function_exists('array_get'))
{
	/**
	 * 递归获取key值
	 * @param string $str: 以 . 隔开
	 * @param array $array: 多维数组
	 * @return mixed|null
	 */
	function array_get($str, $array)
	{
		$list = explode('.', $str);
		$first = array_shift($list);
		$result = isset($array[$first]) ? $array[$first] : null;
		if ($result && is_array($result) && count($list) > 0) {
			return array_get(join('.', $list), $result);
		}
		return $result;
	}
}

if (!function_exists('array_set'))
{
	/**
	 * 动态设置数组值
	 * @param string $str: 以 . 隔开的字符串
	 * @param mixed $val: 值
	 * @param array $array: 引用,目标数据
	 * @return array
	 */
	function array_set($str, $val, &$array)
	{
		$list = explode('.', $str);
		$first = array_shift($list);
		if (count($list) > 0) {
			return array_set(join('.', $list), $val, $array[$first]);
		}
		$array[$first] = $val;
		return $array;
	}
}


/**
 * 过滤异常信息
 * @param $e
 * @return array
 */
function filter_err($e)
{
	if ($e instanceof \Throwable) {
		$log['code'] = $e->getCode();
		$log['file'] = $e->getFile();
		$log['line'] = $e->getLine();
		$log['message'] = $e->getMessage();
		$log['trace'] = $e->getTraceAsString();

		$log['file'] = str_replace(ROOT_PATH, "\\", $log['file']);
		$log['trace'] = str_replace(ROOT_PATH, "\\", $log['trace']);
	}
	else {
		$log['code'] = 0;
		$log['file'] = "";
		$log['line'] = "";
		$log['message'] = print_r($e, true);
		$log['trace'] = "";
	}
	return $log;
}

function env($key, $default = null) {

}

/** ********************************************************* **/


/**
 * @param mixed $data
 * @param string $tag
 * @param string $file
 * @return bool
 * 忽略异常，不抛出异常，不中断程序运行
 */
function put_file_contents($data = null, $tag = "", $file = null)
{
	try {
		$time = date("y-m-d H:i:s");

		if ($data instanceof \Throwable) {
			$data = var_export(filter_err($data), true);
		}
		elseif (!is_string($data)) {
			$data = var_export($data, true);
		}

		$tag = $tag ? " [$tag]". PHP_EOL : "";

		$data = PHP_EOL . "【" . $time . "】====================" . PHP_EOL . $tag . $data . PHP_EOL;

		$file = $file ?: ROOT_PATH . 'storage/logs/logs.txt';
		$path = pathinfo($file, PATHINFO_DIRNAME);
		if (!is_dir($path)) {
			mkdir($path, 666, true);
		}

		file_put_contents($file, $data, FILE_APPEND);
		return true;
	}
	catch (\Throwable $e) {

	}
	return false;
}

/**
 * 生成唯一随机数
 * @param int $lenght
 * @return bool|string
 * @throws Exception
 */
function uniqid_real($lenght = 13)
{
	if (function_exists("random_bytes")) {
		$bytes = random_bytes(ceil($lenght / 2));
	}
	elseif (function_exists("openssl_random_pseudo_bytes")) {
		$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
	}
	else {
		throw new Exception("生成失败");
	}
	return substr(bin2hex($bytes), 0, $lenght);
}

/**
 * 允许指定的 USER_AGENT 显示 Debug 信息
 * @param array $config
 * @return bool
 */
function allow_debug_ua($config = null)
{
	$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
	if (!$ua) {
		return false;
	}
	$allowDebugUaKey = isset($config["allow_debug_ua"]) ? $config["allow_debug_ua"] : get_config("allow_debug_ua");
	if (!$allowDebugUaKey || strstr($ua, $allowDebugUaKey) === false) {
		return false;
	}
	return true;
}

/** ********************************************************* **/


/**
 * 发送 http 请求. 2018-12-29
 *
 * @param $url : url
 * @param array|string $data : 要发送的数据，如：["id" => 1, "name" => "abc"]，或 "id=1&name=abc"
 * @param string $method : 请求方法，GET、POST、JSON、FILE
 * @param int $timeout : 超时秒数
 * @param array $options
 * $options 格式：
	$options = [
		"headers" => [
			"Origin" => "http://www.xxx.com",
			"Referer" => "https://www.xxx.com/xxx",
			"User-Agent" => "xxx",
			"Authorization" => "Bearer xxx",
		],
		"cookies" => "",
		"auth" => "user:pass",
		"auth" => ["user", "pass", "basic"],
		"proxy" => "tcp://example.com:8125"
	];
 *
 * @return array: ["code" => 200, "message" => "", "data" => "", "headers" => "", "response" => "", "error" => ""]
 * code 是 http 状态码
 * message 是异常时的错误提示
 * data 是目标URL返回的内容，html 或 json
 * @throws
 */
function http_send($url, $data = [], $method = "get", $options = [], $timeout = 30)
{
	$return = ["code" => 0, "message" => "", "data" => "", "headers" => "", "response" => "", "error" => ""];

	if (! filter_var($url, FILTER_VALIDATE_URL)) {
		$return["message"] = "无效的 url: '$url'";
		return $return;
	}
	$parseUrl = parse_url($url);
	if(empty($parseUrl['scheme']) || !in_array($parseUrl['scheme'], ["http","https","tcp","ftp"]) || empty($parseUrl['host'])) {
		$return["message"] = "Invalid url: '$url'";
		return $return;
	}
	$port = !empty($parseUrl['port']) ? ':'.$parseUrl['port'] : '';
	$origin = $parseUrl['scheme'] . '://' . $parseUrl['host'] . $port;
	$queryStr = isset($parseUrl['query']) ? $parseUrl['query'] : "";

	if(empty($data)) {
		$data = [];
	}
	elseif (is_string($data)) {
		parse_str(trim($data, '?&'), $data);
	}

	$method = strtoupper($method);

	$defaultOptions = [
		'timeout' => $timeout,
		'connect_timeout' => $timeout,
		'http_errors' => true, // 设置成 false 来禁用 HTTP 协议抛出的异常(如 4xx 和 5xx 响应)，默认情况下 HTPP 协议出错时会抛出异常
		'verify' => false, // 设置成 false 不验证证书，可以设置成证书路径：'/path/to/cert.pem'
		'allow_redirects' => true, // 默认为 true，启用最大数量为 5 的重定向；设置成 false 来禁用重定向
		//'force_ip_resolve' => 'v4', // 使用 IPv4
		'headers' => [
			'Origin' => $origin,
			'Host' => $parseUrl['host'],
			'Referer' => $origin,
			'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
		],
	];

	if ($options && is_array($options)) {
		$options = array_replace_recursive($defaultOptions, $options);
	} else {
		$options = $defaultOptions;
	}
	unset($defaultOptions);

	if (!empty($options['auth'])) {
		if (is_string($options['auth'])) {
			$options['auth'] = explode(':', $options['auth']);
		}
	}

	$params = [];

	if ($method == "GET") {
		if ($queryStr) {
			parse_str($queryStr, $queryArr);
			$data = array_merge($queryArr, $data);
		}
		$params['query'] = $data;
	}
	elseif ($method == "POST") { // 发送表单字段
		$params['form_params'] = $data;
	}
	elseif ($method == "JSON") {
		$params['json'] = $data;
		$method = 'POST';
	}
	elseif ($method == "FILE") { // 发送表单文件
		/*
		 * $data 必须是二维数组，格式为：
			[
			    ['name' => 'field_name1', 'contents' => 'field_val'],
			    ['name' => 'file_name2', 'contents' => fopen('/path/to/file', 'r')]
			]
		 */
		if (!isset($data[0]['name'])) {
			return ["code" => 0, "data" => ""];
		}
		$params['multipart'] = $data;
		$method = 'POST';
	}
	else {
		$return["message"] = "不支持 $method";
		return $return;
	}

	try {
		$client = new \GuzzleHttp\Client($options);
		$response = $client->request($method, $url, $params);
		$return["code"] = $response->getStatusCode(); // http 状态码
		$return["data"] = (string)$response->getBody();
		$return["headers"] = $response->getHeaders();
	}
	catch (\GuzzleHttp\Exception\RequestException $re) {
		$return["code"] = $re->getCode();
		$return["message"] = "请求失败";
		$return["error"] = $re->getMessage();
		if ($re->hasResponse()) {
			$return["response"] = $re->getResponse();
		}
	}
	catch (\Exception $e) {
		$return["code"] = $e->getCode();
		$return["message"] = "请求失败";
		$return["error"] = $e->getMessage();
	}

	return $return;
}

/**
 * 请求 API. 2019-05-08
 * @param $url
 * @param array $data
 * @param string $method
 * @param int $timeout
 * @param array $options
 * @return array: ["code" => 1, "message" => "", "data" => ""];
 */
function request_api($url, $data = [], $method = "post", $options = [], $timeout = 10)
{
	$allow_debug_ua = allow_debug_ua();
	$res = http_send($url, $data, $method, $options, $timeout);

	if (!$allow_debug_ua) {
		unset($res["error"]);
	}

	if (!in_array($res["code"], [200, 201, 202, 204, 205, 206])) {
		return $res;
	}

	$json = json_decode($res["data"], true);

	if (!is_array($json)) {
		$json["code"] = 0;
		$json["message"] = "服务异常！";
		$json["response"] = $res["data"];
		return $json;
	}
	if (!isset($json["code"])) {
		$json["code"] = 0;
	}
	if (!isset($json["message"])) {
		$json["message"] = isset($json["msg"]) ? $json["msg"] : "";
	}
	if (!isset($json["data"])) {
		$json["data"] = "";
	}
	if (stristr($json["message"], "Server Error") !== false || $json["code"] == 500) {
		if ($allow_debug_ua) {
			$json["error"] = $json["message"] . (isset($json["error"]) ? ". " . $json["error"] : "");
		}
		$json["message"] = "服务异常";
	}
	if ($allow_debug_ua && isset($res["headers"])) {
		$json["headers"] = $res["headers"];
	}

	return $json;
}
